//Register name spaces used
Ext.namespace('Ext.ux.guid.grid');

/**
 * The propertyrecord used by the property grid.
 * Name is the key of a property, Value is the value of the property and
 * type is type of element. This type flag will control which editor is loaded
 */
 Ext.ux.guid.grid.PropertyRecord = Ext.data.Record.create([
    { name: 'name', type: 'string' }, 'value', 'type', { name: 'sortOrder', type: 'string' }
]);

/**
 * The constructor of property store
 */
Ext.ux.guid.grid.PropertyStore = function(grid, source){
    Ext.ux.guid.grid.PropertyStore.superclass.constructor.call(this,grid,source);
    this.store = new Ext.data.Store({
        recordType : Ext.ux.guid.grid.PropertyRecord
    });
    this.store.on('update', this.onUpdate,  this);
};

/**
 * Constructor used to create a store used to store designer data
 */
Ext.extend(Ext.ux.guid.grid.PropertyStore, Ext.grid.PropertyStore, {
    //@private The json id  
    jsonId: "__JSON__",

    options: "",

    sortBy: "name",

    //featureSort: new Array(8, 7, 5, 4, 3, 1, 9, 2, 6),

    featureSort: new Array("H", "G", "E", "D", "C", "A", "I", "B", "F"),

    storySort: new Array(7, 6, 4, 0, 1, 8, 5, 3, 2),

    taskSort: new Array("P", "F", "N", "M", "K", "G", "E", "I", "B", "O", "L", "D", "H", "C", "A", "J"),

    //taskSort: new Array(16, 6, 14, 13, 11, 7, 5, 9, 2, 15, 12, 4, 8, 3, 1, 10),

    //taskSort: new Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16),

    //taskSort: new Array("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"),


    relSort: new Array(1, 2, 3),

    itSort: new Array(1, 3, 2),

    stSort: new Array(1),

    /**
    * Get a property type for given name
    * @param {string} name The property to look fore
    */
    getPropertyType: function(name) {
        if (this.grid && this.grid.getPropertyType)
            return this.grid.getPropertyType(name);
        return null;
    },

    /**
    * protected  - should only be called by the grid.  Use grid.setSource instead.
    * Based on source object a property record is created
    * @param {object} o The source object 
    */
    setSource: function(o) {
        this.source = o;
        var sortOrder;
        var readOnly = undefined;

        if (this.grid.initialConfig.containerType == "Project" && o.etype == "card") {
            sortOrder = this.featureSort;
        } else if (this.grid.initialConfig.containerType == "Project") {
            sortOrder = this.relSort;
        } else if (this.grid.initialConfig.containerType == "Release" && o.etype == "card") {
            sortOrder = this.storySort;
        } else if (this.grid.initialConfig.containerType == "Release") {
            sortOrder = this.itSort;
        } else if (this.grid.initialConfig.containerType == "Iteration" && o.etype == "card") {
            sortOrder = this.taskSort;
        } else if (this.grid.initialConfig.containerType == "Iteration") {
            sortOrder = this.stSort;
        }

        if (o.etype == "Iteration" && IsDefaultStatus(o.propConfig.Name)) {
            readOnly = true;
        }

        o = o.propConfig;

        this.store.removeAll();
        var data = [];
        var cnt = 0;
        for (var k in o) {
            if (ExcludeCm(k)) continue;
            if (k.substring(k.length - 2, k.length) == "ID") continue;
            var orgKey = (k.indexOf(this.jsonId) == 0 && k != this.jsonId) ?
                 k.substring(this.jsonId.length) : null;
            if (orgKey && o[orgKey] == undefined) k = orgKey;
            var v = o[this.jsonId + k];
            var type = null;
            var dateFormat = null;
            if (typeof (v) == 'object') {
                type = v.type || type;
                v = v.display || v.value;
            }
            if (typeof (o[k]) == 'string' && o[k].indexOf('/Date(') > -1) {
                v = formatMSDate(o[k]);
            } else if (k.substring(k.length - 3, k.length) == "Sum") {
                v = formatTime(o[k]);
            }

            if (k == "Status")
                v = formatStatus(o[k]);

            if (o[k] == null && k.indexOf('Date') > -1) {
                data.push(new Ext.grid.PropertyRecord({ name: k, value: v || "", type: 'date', sortOrder: sortOrder[cnt] }, k));
            } else if (o[k] == null) {
                data.push(new Ext.grid.PropertyRecord({ editable: false, name: k, value: v || "", type: type, sortOrder: sortOrder[cnt] }, k));
            }
            else if (typeof (o[k]) == 'function') {
                data.push(new Ext.grid.PropertyRecord({ name: k, value: v || String(o[k]), type: 'function', sortOrder: sortOrder[cnt] }, k));
            } else if (typeof (o[k]) == 'object') {
                //data.push(new Ext.grid.PropertyRecord({ name: k, value: v || String(Ext.ux.JSON.encode(o[k])), type: 'object', sortOrder: sortOrder[cnt] }, k));
            } else if (Ext.isDate(v)) {
                data.push(new Ext.grid.PropertyRecord({ name: k, value: v, type: 'date', sortOrder: sortOrder[cnt] }, k));
            }
            else {
                data.push(new Ext.grid.PropertyRecord({ name: k.replace("_", " ").replace("_", " "), value: v || o[k], type: type, sortOrder: sortOrder[cnt] }, k));
            }
            cnt++;
        }
        this.store.loadRecords({ records: data }, {}, true);

        if (readOnly) this.grid.el.mask();
        else this.grid.el.unmask();
    },

    /**
    * @private Update the data and fire beforepropetychange and propertychange events
    * @param {object} ds The datastore
    * @param {object} record The record
    * @param {String} The edit mode
    */
    onUpdate: function(ds, record, type) {
        if (type == Ext.data.Record.EDIT) {
            var v = record.data['value'];
            var oldValue = record.modified['value'];

            var prop = this.grid.getPropertyType(record.id);
            if (prop) {
                var values = prop.values.values || prop.values;
                this.updateSource(record.id + 'ID', v, record.data.type);
                for (i = 0; i < values.length; i++) {
                    var item = values[i];
                    if (item['ID'] == v)
                        v = item[record.id] || item.Name;
                }
            }
            //if (Ext.isDate(v)) v = v.format(Date.patterns.DisplayDate);
            //if (Ext.isDate(oldValue)) oldValue = oldValue.format(Date.patterns.DisplayDate);
            if (this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false) {
                record.data.changeValue = this.updateSource(record.id, v, record.data.type);
                record.commit();
                this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue, this.options);
            } else {
                record.reject();
            }
        }
    },

    /**
    * @private Update a source value property
    * @param {String} key The property key to change
    * @param {String} value The value to change
    * @param {String} type The property type
    */
    updateSource: function(key, value, type) {
        var propType = this.getPropertyType(key);
        if (!type && propType) type = propType.type;
        if (this.grid.fireEvent('propertyvalue', this.source, key, value, type, propType)) {
            this.source[key] = value;
        }
        return this.source[key];
    },

    /**
    * Set the value of a property
    * @param {String} prop The property to set
    * @param {String} value The value to be set
    */
    setValue: function(prop, value) {
        this.store.getById(prop).set('value', value);
        this.updateSource(prop, value);
    }

});


/**
 * Constructior which extends default PropertyColumnModel in so that 
 * we implement other editors
 */
Ext.ux.guid.grid.PropertyColumnModel = function(grid, store){
    Ext.ux.guid.grid.PropertyColumnModel.superclass.constructor.call(this,grid,store);
    this.setConfig( [
        {header: this.nameText, width:40, resizable:true, sortable: true, dataIndex:'name', id: 'name', menuDisabled:true},
        {header: this.valueText, width:60, resizable:false, dataIndex: 'value', id: 'value', menuDisabled:true}
    ]);
    this.jsonId = grid.jsonId;
    Ext.apply(this.editors, {
    //'editlist': new Ext.grid.GridEditor(new Ext.ux.form.SimpleCombo({ forceSelection: false, data: [], editable: true, customProperties: true })),
    'combo': new Ext.grid.GridEditor(new Ext.form.ComboBox({ forceSelection: false, editable: true, valueField: 'id', displayField: 'name', typeAhead: true, mode: 'local', triggerAction: 'all' })),
    //'list': new Ext.grid.GridEditor(new Ext.ux.form.SimpleCombo({ forceSelection: false, data: [], editable: true, customProperties: false })),
    'color': new Ext.grid.GridEditor(new Ext.ux.ColorField({ hiddenName: 'pref_sales', showHexValue: true })),
    'readOnly': new Ext.grid.GridEditor(new Ext.form.TextField({ readOnly:true }))
    });
    this.valueRendererDelegate = this.valueRenderer.createDelegate(this);
    this.propertyRendererDelegate = this.propertyRenderer.createDelegate(this);
};

/**
 * Extend the default PropertyColumnModel in so that we implement other editors
 */
Ext.extend(Ext.ux.guid.grid.PropertyColumnModel, Ext.grid.PropertyColumnModel, {
    /**
    * Get a property type for given name
    * @param {string} name The property to look fore
    */
    getPropertyType: function(name) {
        if (this.grid && this.grid.getPropertyType) return this.grid.getPropertyType(name);
        return null;
    },

    /**
    * Get the celleditor by row and colIndex
    * @param {int} colIndex The column index
    * @param {int} rowIndex The row index
    * @returns {Object} The cellEditor
    */
    getCellEditor: function(colIndex, rowIndex) {
        var p = this.store.getProperty(rowIndex);
        var n = p.data['name'], val = p.data['value'], t = p.data['type'];
        if (this.grid.customEditors[n]) {
            return this.grid.customEditors[n];
        }
        if (n.substring(n.length - 3, n.length) == "Sum" || n == "Status" || n == "Velocity" || n == "Task Count" || n == "Story Size" || n == "Full Story Size" || n == "Finished Story Size" || n == "Finished Percentage") return this.editors['readOnly']; ;
        var prop = this.getPropertyType(n);

        var depnt = this.getPropertyType("Consignee");
        if (prop && prop.values && depnt.values.length == 0) return this.editors['readOnly'];
        
        //Check if there is a property and if there is a editor
        if (prop) {
            if (prop.editor) {
                if (typeof (prop.editor) != 'string') {
                    return prop.editor;
                }
                t = prop.editor;
            }
            t = t || prop.type;
            if (!t && prop.values) {
                var items = [];
                var values = prop.values.values || prop.values;

                for (i = 0; i < values.length; i++) {
                    var item = values[i];
                    if (prop.values.filterBy && item[prop.values.filterBy].toLowerCase() == this.grid.getSource().containerId.toLowerCase()) {
                        items.push([item.ID, item.Name]);
                    } else if (!prop.values.filterBy) items.push([item.ID, item.Name]);
                }

                //var editor = prop.editable ? this.editors['editlist'] : this.editors['list'];
                //editor.field.setList(stories);
                var store = new Ext.data.SimpleStore({
                    fields: ['id', 'name'],
                    data: items
                });

                var cbo = new Ext.grid.GridEditor(new Ext.form.ComboBox({ hiddenName: 'yoga', valueField: 'id', displayField: 'name', typeAhead: true, mode: 'local', triggerAction: 'all' }));
                cbo.field.store = store;

                return cbo;
            } else if (!t) {
                var editor = this.editors['color'];
                return editor;
            }

        }

        if (t && this.editors[t]) {
            return this.editors[t];
        } else if (Ext.isDate(val)) {
            return this.editors['date'];
        } else if (typeof val == 'number') {
            return this.editors['number'];
        } else if (typeof val == 'boolean') {
            return this.editors['boolean'];
        } else if (typeof val == 'string') {
            return this.editors['string'];
        }
        return this.defaultEditor || this.editors[prop ? 'string' : 'mixed'];
    },


    /**
    * Format the value of cell
    * @param {object} The value to be formated
    * @param {Element} The element
    * @param {Record} The record
    */
    valueRenderer: function(value, p, r) {
        if (typeof value == 'boolean') {
            p.css = (value ? "typeBoolTrue" : "typeBoolFalse");
            return (value ? "True" : "False");
        }
        if (Ext.isDate(value)) {
            return value.format(Date.patterns.DisplayDate);
        }
        var propType = this.getPropertyType(r.id);
        if (propType && ['object', 'array', 'object/array'].indexOf(propType.type) != -1) {
            p.css = "typeObject";
        }
        if (typeof (value) == "string" && value.length > 24) {
            value = value.substring(0, 21) + "...";
        }
        //Decode the value for html
        var div = document.createElement('div');
        div.appendChild(document.createTextNode(value));
        return div.innerHTML;
    },

    /**
    * @private Implement a tooltip
    */
    propertyRenderer: function(value, p) {
        var propType = this.getPropertyType(value);
        if (propType) {
            qtip = propType.desc || '';
            p.attr = 'qtip="' + qtip.replace(/"/g, '&quot;') + '"'; //'
        }
        return value;
    },

    /**
    * @private Return one of the property renders
    */
    getRenderer: function(col) {
        return col == 0 ? this.propertyRendererDelegate : this.valueRendererDelegate;
    }
});


/**
 * Create a property gird by extending a Grid
 * @type component
 */
Ext.ux.guid.grid.PropertyGrid = Ext.extend(Ext.grid.EditorGridPanel, {
    // @private columns cannot be moved
    enableColumnMove: false,

    propertyLoaded: false,
    // @private Rows will not be stripped
    stripeRows: false,
    // @private Mouse over events are not tracked
    trackMouseOver: false,
    // @private Single click will start edit
    clicksToEdit: 1,
    // @private noHD menu
    enableHdMenu: false,
    // @private Grid is forced to fit space
    viewConfig: { forceFit: true },
    // @private The internal jsonId used
    jsonId: '__JSON__',

    /**
    * Should it use codePress as code editor (defaults true)
    * @type {Boolean}
    @cfg */
    codePress: true, //codePress enabled

    /**
    * Location of CodePress
    * @type {String}
    @cfg */
    codePressPath: undefined,

    /**
    * Get a property type for given name
    * @param {string} name The property to look fore
    */
    getPropertyType: function(name) {
        if (this.propertyTypes) {
            var i = this.propertyTypes.find('name', name);
            if (i != -1) return this.propertyTypes.getAt(i).data;
        }
        return null;
    },

    getProperties: function() {
        //do ajax request here.

        Ext.Ajax.request({
            url: '../User/GetAll',
            method: 'POST',
            params: { projectId: Ext.get('projectId').dom.value },
            success: function(response) {
                var responseMessage = Ext.util.JSON.decode(response.responseText);
                this.createProperties(responseMessage);
            },
            failure: function(response) {
                Ext.MessageBox.alert("Failure, please try again", response.responseText);
            },
            scope: this
        });
    },

    createProperties: function(json) {
        var users;
        var usrValues = [];

        if (json) users = json.users;
        else users = [];

        if (users.length > 0) {
            var nullUser = Ext.util.JSON.decode(Ext.util.JSON.encode(users[0]));
            nullUser.UserID = null;
            nullUser.ID = null;
            nullUser.Name = "None";
            users.splice(0, 0, nullUser);
        }

        this.propertyTypes = new Ext.data.JsonStore({
            data: { properties: [{
                name: "Consignee",
                desc: "User to which assign Task",
                values: users
            }, {
                name: "Story",
                desc: "Story to which Task belongs to"
            }, {
                name: "Feature",
                desc: "Feature to which Story belongs to"
            }, {
                name: "Color",
                type: "color",
                desc: "Choose color"
}]
            },
            sortInfo: { field: 'sortOrder', order: 'ASC' },
            root: 'properties',
            fields: ['name', 'type', 'defaults', 'desc', 'instance', 'editable', 'values', 'editor', 'sortOrder']
        });

        this.propertyLoaded = true;
    },


    /**
    * @private The component and events
    */
    initComponent: function() {
        //this.getProperties();
        this.customEditors = this.customEditors || {};
        this.lastEditRow = null;
        var store = new Ext.ux.guid.grid.PropertyStore(this);
        store.jsonId = this.jsonId,
        this.propStore = store;
        var cm = new Ext.ux.guid.grid.PropertyColumnModel(this, store);
        store.store.sort('sortOrder', 'ASC');
        this.addEvents(
        /**
        * Event fired before value changes
        * @param {Source} source The source object
        * @param {String} key The key changed
        * @param {String} value The value to be set
        * @param {String} type The type of value
        * @return {Boolean} False will assume custom assignment
        */
            'beforepropertychange',
        /**
        * Event fired when propery changed
        * @param {Source} source The source object
        * @param {String} key The key changed
        * @param {String} value The value to be set
        * @param {String} type The type of value
        * @return {Boolean} False will assume custom assignment
        */
            'propertychange',
        /**
        * Event fired to allow custom change of value
        * @param {Source} source The source object
        * @param {String} key The key changed
        * @param {String} value The value to be set
        * @param {String} type The type of value
        * @return {Boolean} False will assume custom assignment
        */
            'propertyvalue'
        );
        this.cm = cm;
        this.ds = store.store;
        Ext.ux.guid.grid.PropertyGrid.superclass.initComponent.call(this);

        this.selModel.on('beforecellselect', function(sm, rowIndex, colIndex) {
            if (colIndex === 0) {
                this.startEditing.defer(200, this, [rowIndex, 1]);
                return false;
            }
        }, this);
    },

    /**
    * @private Apply the property grid style
    */
    onRender: function() {
        Ext.ux.guid.grid.PropertyGrid.superclass.onRender.apply(this, arguments);
        this.getGridEl().addClass('x-props-grid');
    },

    /**
    * @private After render the source is set
    */
    afterRender: function() {
        Ext.ux.guid.grid.PropertyGrid.superclass.afterRender.apply(this, arguments);
        if (this.source) {
            this.setSource(this.source);
        }
    },

    /**
    * Set the source of the grid
    * @param {Object} The source object
    */
    setSource: function(source) {
        this.propStore.setSource(source);
    },

    /**
    * Get the datasource of the grid
    * @returns {Object} The datasource object
    */
    getSource: function() {
        return this.propStore.getSource();
    }
});

//Is not registered but required by designer
Ext.reg('guidpropertygrid', Ext.ux.guid.grid.PropertyGrid);
